package com.cby.library.tool.log

import android.content.Context
import android.content.pm.PackageManager
import com.cby.library.tool.BuildConfig
import com.cby.library.tool.util.NetDeviceUtils
import java.io.*
import java.lang.StringBuilder
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.HashMap

/**
 * @description: LogManager
 *
 * @author ChenBaoyang
 * @date 2021/9/6 10:18
 */
class CbyLogManager private constructor() {

    /**
     * 日志监听开关
     */
    private var logOpen = false

    /**
     * 上下文
     */
    private var context: Context? = null

    /**
     * 是否记录所有日志信息
     */
    private var isAll: Boolean = false

    /**
     * 日志过滤的tag集合
     */
    private val logTagMap = HashMap<String, String>()

    private var logTime: String? = null
    private var logHead: String? = null
    private var versionName: String? = null
    private var versionCode: String? = null

    companion object{
        /** LogManager实例对象 */
        val instance = SingleTon.holder

        /** logcat 命令*/
        private const val COMMAND_LOGCAT = "logcat"

        /** "*:S"表示监听所有的信息 */
        private const val COMMAND_COLLECT_ALL = "*:S"

        /** -v：设置log的打印格式，默认的是brief 格式 */
        private const val COMMAND_OPTION_V = "-v"

        /** -d：得到所有log并退出 (不阻塞). */
        private const val COMMAND_OPTION_D = "-d"

        /** "I"表示监听的Log类型，当然这里还可以写其它类型 。VERBOSE(v) 、DEBUG(d)、 INFO(i)、 WARN(w)、 ERROR(e)， 不过须要与监听的与Tag一一对称才可以。*/
        private const val COMMAND_LEVEL_I = "I"

        /** "I"表示监听的Log类型:VERBOSE(v) */
        private const val COMMAND_LEVEL_V = "V"

        private const val COMMAND_SEPARATOR = ":"

    }

    private object SingleTon{
        val holder = CbyLogManager()
    }

    /**
     * 初始化调用，开启日志采集
     * @param context Context
     */
    fun init(context: Context) {
        // 开启日志监听
        logOpen = true
        this.context = context.applicationContext
        initLogHead(context)
        Thread(runnable).start()

    }

    /**
     * 关闭log线程
     */
    fun release() {
        logOpen = false
    }

    fun addLogTag(tag: String, level: String) {
        logTagMap[tag] = level
    }

    /**
     * 设置录制所有的日志信息
     * @param isAll Boolean
     */
    fun setAll(isAll: Boolean) {
        this.isAll = isAll
    }

    /**
     * 创建日志获取命令
     *
     * @return Array<String>
     */
    private fun createRunningCommand(): Array<String> {
        val sb = StringBuilder()
        logTagMap.forEach {
            sb.append("${it.key}:${it.value}")
            sb.append(" ")
        }
        sb.append(COMMAND_COLLECT_ALL)
        return if(isAll) {
            arrayOf("logcat", COMMAND_OPTION_V, sb.toString())
        }else{
            arrayOf("logcat", sb.toString())
        }

    }

    /**
     * 日志的获取线程
     */
    private val runnable: Runnable by lazy {
        Runnable {
            while (logOpen) {
                // getExternalFilesDir("logs") = /storage/emulated/0/Android/data/packageName/files/logs
                val logFilePath = context?.getExternalFilesDir("logs")?.absolutePath
                try {
                    //tag:I *:S
                    //val running = arrayOf("logcat", "$tag:V *:S")
                    // “logcat” 就是我们主要的logcat命令
                    // LogTestActivity:I Jason:V 选择要过滤的tag的log信息，可以配置多个规则条件
                    // "I 、E"表示监听的Log类型，当然这里还可以写其它类型 。VERBOSE(v) 、DEBUG(d)、 INFO(i)、 WARN(w)、 ERROR(e)， 不过须要与监听的与Tag一一对称才可以。
                    //"*:S"表示监听所有的信息，这里表示只要tag是TextLogDemoTag,Logcat类型为E 的 所有Log都会被获取到。
                    //Jason:E *:S这是一套规则，当然方法允许多个规则
                    //val running = arrayOf("logcat",  "LogTestActivity:I $tag:V *:S" )
                    val running = createRunningCommand()
                    val mLogcatProc = Runtime.getRuntime().exec(running)
                    val reader = BufferedReader(InputStreamReader(mLogcatProc.inputStream))
                    var line: String
                    while (reader.readLine().also { line = it } != null) {
                        if (logFilePath != null) {
                            createLogFile(logFilePath, line)
                        }
                    }
                } catch (e: Exception) {
                    e.printStackTrace()
                }
                Thread.sleep(100L)
            }
        }
    }


    /**
     * 直接将信息存入日志文件中
     *
     * @param text String
     */
    fun write2LogFile(text: String) {
        val runnable = Runnable {
            val logFilePath = context?.getExternalFilesDir("logs")?.absolutePath
            if (logFilePath != null) {
                createLogFile(logFilePath, text)
            }
        }
        val thread = Thread(runnable)
        thread.start()
    }


    private val sdf = SimpleDateFormat("yyyy-MM-dd")
    /**
     * 获取当天的日期
     */
    private val todayDate: String
        get() {
            return sdf.format(Date(System.currentTimeMillis()))
        }

    var dataFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA)
    /**
     * 初始化奔溃信息的头部内容
     *
     * @param context  上下文
     */
    private fun initLogHead(context: Context) {
        //崩溃时间
        logTime = dataFormat.format(Date(System.currentTimeMillis()))
        //版本信息
        try {
            val pm = context.packageManager
            val pi = pm.getPackageInfo(
                context.packageName,
                PackageManager.GET_CONFIGURATIONS
            )
            if (pi != null) {
                versionName = pi.versionName
                versionCode = pi.versionCode.toString()
            }
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
        }
        //组合Android相关信息
        val sb = StringBuilder()
        sb.append("\n软件App的Id:").append(context.packageName)
        sb.append("\n是否是DEBUG版本:").append(BuildConfig.BUILD_TYPE)
        sb.append("\n是否root:").append(NetDeviceUtils.isDeviceRooted())
        sb.append("\n系统硬件商:").append(NetDeviceUtils.getManufacturer())
        sb.append("\n设备的品牌:").append(NetDeviceUtils.getBrand())
        sb.append("\n手机的型号:").append(NetDeviceUtils.getModel())
        sb.append("\n设备版本号:").append(NetDeviceUtils.getId())
        sb.append("\nCPU的类型:").append(NetDeviceUtils.getCpuType())
        sb.append("\n系统的版本:").append(NetDeviceUtils.getSDKVersionName())
        sb.append("\n系统版本值:").append(NetDeviceUtils.getSDKVersionCode())
        sb.append("\n当前的版本:").append(versionName).append("—")
            .append(versionCode)
        sb.append("\n\n")
        logHead = sb.toString()
    }


    /**
     * 创建日志文件
     * @param path String
     * @param text String
     */
    private fun  createLogFile(path: String, text: String) {
        //synchronized(this){
            val appDir = File(path)
            if (!appDir.exists()) {
                appDir.mkdirs()
            }
            val fileName = "$todayDate.txt"
            val file = File(appDir, fileName)
            if (!file.exists()) {
                file.createNewFile()
            }
            try
            {
                //BufferedWriter for performance, true to set append to file flag
                val buf = BufferedWriter( OutputStreamWriter(FileOutputStream(file, true), "gbk"))
                if(!file.exists()) {
                    buf.append(logHead)
                }
                buf.append(text)
                buf.newLine()
                buf.close()
            }
            catch (e:IOException)
            {
                e.printStackTrace();
            }
        //}

    }

    /**
     * 读取当天日志文件的内容
     * @param callback OnLogCallBack
     */
    fun getLogInfoFromFile(callback: OnLogCallBack) {
        val logFilePath = context?.getExternalFilesDir("logs")?.absolutePath
        val fileName = "$todayDate.txt"
        Thread{
            val log = logFilePath?.let { readLogFile(it, fileName) }
            callback.onLog(log)
        }.start()
    }

    /**
     * 异步读取日志信息
     * @param path String
     * @param fileName String
     * @return String
     */
    private fun readLogFile(path: String, fileName: String) : String {

        val file = File(path, fileName)
        if(!file.exists()) {
            return ""
        }
        var text = "";
        try {
            val fis = FileInputStream(file)
            val ins = BufferedInputStream(fis)

            val reader = BufferedReader(InputStreamReader(ins, "GBK"))

            var str = reader.readLine();

            while (str != null) {
                text = "$text$str \n"
                str = reader.readLine()
            }
            reader.close();

        } catch (e:FileNotFoundException) {
            e.printStackTrace()
        } catch (e:IOException) {
            e.printStackTrace()
        }
        return text
    }



    interface OnLogCallBack{
        fun onLog(logInfo: String?)
    }
}